
#include "clib_hash_adler32.h"


//小于 65536 的最大素数
#define CLIB_HASH_ALDER32_BASE_VALUE (65521)

// 使 255n(n+1)2 + (n+1)(BASE-1) <= 2^32-1 成立的最大的n
#define CLIB_HASH_ALDER32_MAX_N (5552)

// dot
#define CLIB_HASH_ALDER32_DO1(data, i)    {adler += (data)[i]; sum2 += adler;}
#define CLIB_HASH_ALDER32_DO2(data, i)    CLIB_HASH_ALDER32_DO1(data, i); CLIB_HASH_ALDER32_DO1(data, (i) + 1);
#define CLIB_HASH_ALDER32_DO4(data, i)    CLIB_HASH_ALDER32_DO2(data, i); CLIB_HASH_ALDER32_DO2(data, (i) + 2);
#define CLIB_HASH_ALDER32_DO8(data, i)    CLIB_HASH_ALDER32_DO4(data, i); CLIB_HASH_ALDER32_DO4(data, (i) + 4);
#define CLIB_HASH_ALDER32_DO16(data)      CLIB_HASH_ALDER32_DO8(data, 0); CLIB_HASH_ALDER32_DO8(data, 8);

#define CLIB_HASH_ALDER32_MOD(a)          (a) %= CLIB_HASH_ALDER32_BASE_VALUE
#define CLIB_HASH_ALDER32_MOD28(a)        (a) %= CLIB_HASH_ALDER32_BASE_VALUE
#define CLIB_HASH_ALDER32_MOD63(a)        (a) %= CLIB_HASH_ALDER32_BASE_VALUE

clib_uint32_t clib_hash_adler32(clib_uint8_t const *data, size_t size, clib_uint32_t seed) {
    //将 adler-32 拆分为分量总和
    clib_uint32_t adler = seed;
    size_t sum2 = (adler >> 16) & 0xffff;
    adler &= 0xffff;

    //每次一个字节处理
    if (size == 1) {
        adler += data[0];
        if (adler >= CLIB_HASH_ALDER32_BASE_VALUE) {
            adler -= CLIB_HASH_ALDER32_BASE_VALUE;
        }
        sum2 += adler;
        if (sum2 >= CLIB_HASH_ALDER32_BASE_VALUE) {
            sum2 -= CLIB_HASH_ALDER32_BASE_VALUE;
        }
        return (clib_uint32_t) (adler | (sum2 << 16));
    }
    clib_check_if_true_return_value(data == NULL, 1);

    // 每次小于16个字节处理方式
    if (size < 16) {
        while (size--) {
            adler += *data++;
            sum2 += adler;
        }
        if (adler >= CLIB_HASH_ALDER32_BASE_VALUE) {
            adler -= CLIB_HASH_ALDER32_BASE_VALUE;
        }
        CLIB_HASH_ALDER32_MOD28(sum2);
        return (clib_uint32_t) (adler | (sum2 << 16));
    }

    size_t n;
    while (size >= CLIB_HASH_ALDER32_MAX_N) {
        size -= CLIB_HASH_ALDER32_MAX_N;
        n = CLIB_HASH_ALDER32_MAX_N / 16;
        do {
            CLIB_HASH_ALDER32_DO16(data);
            data += 16;

        } while (--n);

        CLIB_HASH_ALDER32_MOD(adler);
        CLIB_HASH_ALDER32_MOD(sum2);
    }
    if (size) {
        while (size >= 16) {
            size -= 16;
            CLIB_HASH_ALDER32_DO16(data);
            data += 16;
        }
        while (size--) {
            adler += *data++;
            sum2 += adler;
        }
        CLIB_HASH_ALDER32_MOD(adler);
        CLIB_HASH_ALDER32_MOD(sum2);
    }
    return (clib_uint32_t) (adler | (sum2 << 16));
}


clib_uint32_t clib_hash_adler32_from_string(char const *string, clib_uint32_t seed) {
    clib_check_if_true_return_value(string == NULL, 0);
    return clib_hash_adler32((clib_uint8_t const *) string, strlen(string) + 1, seed);
}